package org.noote.libs.camera.broadcast;

import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.util.Locale;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.noote.libs.camera.CameraInterface;
import org.noote.libs.camera.listener.CameraPreviewListener;
import org.noote.libs.graphicbuffer.GraphicBuffer;
import org.noote.libs.tcpip.IPServer;
import org.noote.libs.tcpip.IPEvent_Server;
import org.noote.libs.tcpip.IPServer_ConnectedThread;

import android.content.Context;
import android.graphics.Bitmap;
import android.util.Base64;
import android.util.Log;

public class CameraBroadcast {

	boolean _isActive = false;
	final GraphicBuffer _frame = new GraphicBuffer();	
	protected final Lock frameLock = new ReentrantLock();
	protected IPServer _server = null;
	private onEventListener _listener = null;
	private CameraPreviewListener _userCameraListener = null;
	private CameraInterface _camera = null;
	protected int _cameraId = 0;
	
	public interface onEventListener {
		public void onConnected(String sIPClient);
		public void onDisconnected(String sIPClient);
		public void onError(String sMessage);
	}
	
    class myIPServer extends IPServer
	{
    	myIPServer()
    	{
    		super();
    	}
    	
    	@Override
    	public IPEvent_Server createServerEvents()
    	{
    		return new IPEvent_Server()
				{
    			static final String TAG = "CameraBroadcast.IPServer.IPEvent_Server";
    			private boolean _isDebug = true;
    			int _iCurrentConnection = 0;
    			IPServer_ConnectedThread _thread=null;

    			@Override
    			public void setThread(IPServer_ConnectedThread thread)
    			{
    				this._thread = thread;  
    			}

    			@Override
    			public void onConnected() {
    				 InetAddress inetclient = _thread.getClientAddress();
    				 
    				_isActive=(++_iCurrentConnection)>0;
    				if(_listener != null)
    					_listener.onConnected(inetclient.getHostName());
    			}
    			
    			@Override
    			public void onDisconnected() {
    				InetAddress inetclient = _thread.getClientAddress();
    				
    				_isActive=(--_iCurrentConnection)>0;
    				if(_listener != null)
    					_listener.onDisconnected(inetclient.getHostName());
    			}
    			
    			@Override
    			public void onError(String sError) {
    				Log.e(TAG, sError);
    				if(_listener != null)
    					_listener.onError(sError);
    			}
    			
    			@Override
    			public void onText(String sText) {
    				if(_isDebug)
    					Log.i(TAG, sText);
    			}
    			
    			@Override
    			public void onCmd(String sCmd) {
    	        	if(_isDebug)
    	        		Log.d(TAG, "Get cmd : "+sCmd);
    	    		String [] sCmds = sCmd.split("\\:");
    	    		if(sCmds.length==2)
    	    		{
    	    			String sOrder = sCmds[0].trim().toLowerCase(Locale.getDefault());
    	    			String sOps = sCmds[1].trim();
    	    			if(sOrder.equals("disconnect"))
    	    			{
    	    				// TODO close connection !
    	    			}
    	    			else if(sOrder.equals("jpg_preview"))
    		        	{
    		        		int compressRatio = 30;
    		        		
    		    			String [] sDatas = sOps.split("\\,");
    		        		for(String d : sDatas)
    		        		{
    		        			String [] sVals = d.split("\\=");
    		        			if(sVals.length != 2) continue;

    		        			String name = sVals[0].trim().toLowerCase(Locale.getDefault());
    		        			String val = sVals[1].trim();
    		        			
    		    	        	if(name.equals("ratio"))
    		    	        	{
    		    	        		compressRatio = Integer.parseInt(val);
    		    	        	}
    		            	}

    		        		onAskJPGPreview(compressRatio);
    		        	}
    	        	}
    			}
    			
    			protected void onAskJPGPreview(int compressRatio) {
    				
    				if(compressRatio<0 || compressRatio>100)
    				{
    					Log.e(TAG, "invalid ratio '"+compressRatio+"' !");
    					return;
    				}
    				if(!_isActive)
    				{
    					Log.e(TAG, "not active !");
    					return;
    				}
    				if(_frame.isVoid())
    				{
    					// no frame yet ?
    					Log.e(TAG, "no frame yes, wait for it...");
    					int count = 3;
    					while(_frame.isVoid() &&count>0)
    					{
	    					try {
	    						Thread.sleep(150);
	    	                } catch (InterruptedException e) {
	    	                    e.printStackTrace();
	    	                    Log.e(TAG, "cannot wait !");
	    	                    return;
	    	                }
	    					Log.w(TAG, "wait for frame !");
	    					count--;
    					}
    					if(_frame.isVoid())
    					{
    						Log.e(TAG, "cannot get frame !");
    						return;
    					}
    				}
    				if (frameLock.tryLock()) {
    					try {
    						// Create temp bitmap
    						Bitmap bitmapFrame = Bitmap.createBitmap(_frame.getWidth(), _frame.getHeight(), Bitmap.Config.ARGB_8888);
    						if(bitmapFrame != null)
    						{
    							// copy frame in bitmap
    							bitmapFrame.setPixels(_frame.getBuffer(), 0, _frame.getWidth(), 0, 0, _frame.getWidth(), _frame.getHeight());
	    						
	    						// Create JPEG conversion
	    						final ByteArrayOutputStream os = new ByteArrayOutputStream();
	    						bitmapFrame.compress(Bitmap.CompressFormat.JPEG, compressRatio, os);

	    						// free bitmap ;)
	    						bitmapFrame.recycle();
	    				 
	    						// Do base64 encode
	    						String encodedImage = Base64.encodeToString(os.toByteArray(), Base64.NO_WRAP|Base64.NO_PADDING|Base64.URL_SAFE);
	
	    						// Send data !
	    						_thread.sendCommand("jpg_preview", "ratio="+compressRatio+",data="+encodedImage);
    						}
    						else Log.e(TAG, "Cannot create bitmap !");
    					} finally {
    						frameLock.unlock();
    					}
    				}
    			}
			};
    	}
	}
	public CameraBroadcast(Context context, CameraInterface camera) {
		_camera = camera;
		_userCameraListener = _camera.getPreviewListener();
		_camera.setPreviewListener(new CameraPreviewListener()
		{
			@Override
			public void onStartPreview() {
				if(_userCameraListener != null)
					_userCameraListener.onStartPreview();
			}

			@Override
			public void onGrabbedFrame(GraphicBuffer frame) {
				if(!_isActive) return;
				
				if (frameLock.tryLock()) {
					try {
						// if size have changed, resize _frame
						if(frame.getWidth()!=_frame.getWidth() || frame.getHeight()!=_frame.getHeight())
							_frame._create_outputbuffer(frame.getWidth(), frame.getHeight());
						// copy frame
						System.arraycopy(frame.getBuffer(), 0, _frame.getBuffer(), 0, _frame.getWidth()*_frame.getHeight());
					} finally {
						frameLock.unlock();
					}
				}
				
				if(_userCameraListener != null)
					_userCameraListener.onGrabbedFrame(frame);
			}

			@Override
			public void onStopPreview() {
				if(_userCameraListener != null)
					_userCameraListener.onStopPreview();
			}
		});
		
		_server = new myIPServer();
	}
	
	public boolean isConnected()
	{
		return _server.isConnected();
	}
	public boolean createServer(int iPort)
	{
		return _server.create(iPort);
	}
	public boolean killServer()
	{
		return _server.close();
	}
	
	public void setOnEventListener(onEventListener listener)
	{
		this._listener = listener;
	}
}
